/*
 * $Id: Thread.cc,v 1.13 2006-04-03 08:36:58 bacon Exp $
 */

#include <xpp/bas/Thread.h>
#include <xp/bas/time.h>
#include <xp/bas/assert.h>

namespace xpp
{
	namespace bas
	{

		Thread::Handle Thread::INVALID_HANDLE = XP_THRHND_INVALID;

		Thread::Thread (): thread_target (XP_NULL)
		{
			// NOTICE: as Thread is not asking for user-data space
			//         xp_thr_open should not return an error .
			xp_ensure (
				xp_thr_open (this, (int(*)(xp_thr_t*))XP_NULL, XP_NULL) != XP_NULL);
		}

		Thread::~Thread ()
		{
			xp_assert (this->__state != Thread::RUNNING);
			// it is subclasses' responsibility to stop the thread gracefully.
			// so stop is not called here.
			// stop ();

			/*if (this->__joinable)*/ join ();
			xp_thr_close (this);	
		}

		Thread::Handle Thread::current ()
		{
			return xp_getcurthrhnd ();
		}

		int Thread::start (int flags)
		{
			if (this->__state == Thread::RUNNING) return -1;
			
			this->__joinable = ((flags & Thread::DETACHED) == 0);
			if (create_thread() == -1) {
				this->__state = Thread::INCUBATING;
				this->__handle = Thread::INVALID_HANDLE;
				return -1;
			}

			this->__state = Thread::RUNNING;
			return 0;
		}

		int Thread::stop ()
		{
			// NOTICE:
			// make sure that subclasses override "stop" and call it
			// properly so that the thread can be terminated gracefully.
			// "stop" here just aborts the running thread.
			return xp_thr_stop (this);
		}

		int Thread::create_thread ()
		{
		#ifdef _WIN32
			unsigned int tid;
			if (this->__handle != Thread::INVALID_HANDLE) {
				::CloseHandle (this->__handle);
			}
			this->__handle = (HANDLE)::_beginthreadex (XP_NULL, 0,
				(unsigned int (__stdcall*)(void*))Thread::start_thread,
				this, 0, &tid);
			if (this->__handle == 0) return -1;
		#else
			pthread_attr_t attr;
			::pthread_attr_init (&attr);

			if (this->__joinable) {
				if (::pthread_attr_setdetachstate (
					&attr, PTHREAD_CREATE_JOINABLE) != 0) return -1;
			}
			else {
				if (::pthread_attr_setdetachstate (
					&attr, PTHREAD_CREATE_DETACHED) != 0) return -1;
			}

			if (::pthread_create (
				&this->__handle, &attr, Thread::start_thread, this) != 0) {
				::pthread_attr_destroy (&attr);
				return -1;
			}
			::pthread_attr_destroy (&attr);
		#endif

			return 0;
		}

		void* Thread::start_thread (void* arg)
		{
			Thread* t = (Thread*)arg;

			// Prevent the thread from running before "thread_state" is set
			// to RUNNING in "start". Otherwise, "start_thread" can terminate
			// (thread_state is set to TERMINATED at this point) before
			// "thread_state" is set to RUNNING in "start" so "thread_state"
			// becomes RUNNING even after the thread terminates.
			while (t->__state != Thread::RUNNING) xp_sleep (100);

#ifndef _WIN32
			::pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, (int*)XP_NULL);
			::pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, (int*)XP_NULL);
#endif
			try {
				t->__return_code =
					(t->thread_target != XP_NULL)?
					t->thread_target->run(): t->run ();
				t->__state = Thread::TERMINATED;
			}
			catch (...) {
				t->__state = Thread::ABORTED;
				// the exception is not thrown futhur.
				// and t->__return_code becomes undefined.
			}

#ifdef _WIN32
			_endthreadex (t->__return_code);
#else
			pthread_exit (&t->__return_code);
#endif
			return XP_NULL;
		}

	}
}
